home *** CD-ROM | disk | FTP | other *** search
/ Tech Arsenal 1 / Tech Arsenal (Arsenal Computer).ISO / tek-02 / pas2c.zip / TASK.C < prev    next >
Text File  |  1993-01-04  |  4KB  |  158 lines

  1. /*
  2.  
  3.     Task Switching (Coroutines)
  4.  
  5.     Simple, non-preemptive tasking for Turbo C and Turbo Pascal 4.
  6.     Create tasks using the start_task routine, and switch tasks with
  7.     the switch_task routine. A few utility functions are provided to
  8.     allow enabling and disabling tasks, and to delay a task for a
  9.     specified time interval.
  10.  
  11. */
  12.  
  13. #include <std.h>
  14. #include <dos.h>
  15. #include <process.h>
  16. #include <setjmp.h>
  17. #include <stdlib.h>
  18.  
  19. #include "task.h"
  20.  
  21. #define MAX_TASKS 16
  22.  
  23. LOCAL TASK FAR (*task_start)(VOID);
  24. LOCAL jmp_buf tasks[MAX_TASKS];
  25. LOCAL BOOL task_active[MAX_TASKS];
  26.  
  27. /* publish current_task variable if not compiling for Turbo Pascal. if
  28.    Turbo Pascal 4 is being used, a global integer called _current_task must
  29.    must be provided */
  30. #ifndef PASCAL
  31. COUNT current_task;
  32. #endif
  33.  
  34. /* switch to next task - on 8Mhz 80286, takes 90 usecs to go from task
  35.    to task, 210 usec to skip 16 inactive tasks (without call overhead) */
  36. VOID PASCAL FAR switch_task(VOID)
  37.     {
  38.     COUNT i;
  39.  
  40.     /* save current task */
  41.     if (setjmp(tasks[current_task]) == 0)
  42.         {
  43.         /* select next active task */
  44.         for (i = (current_task + 1) % MAX_TASKS;
  45.                 !task_active[i]; i = (i + 1) % MAX_TASKS)
  46.             ;
  47.         /* start next task */
  48.         current_task = i;
  49.         longjmp(tasks[current_task], 1);
  50.         }
  51.     /* return to next task */
  52.     }
  53.  
  54. /* create and start a new task */
  55. VOID PASCAL FAR start_task(COUNT id, VOID *stack, TASK FAR (*task)())
  56.     {
  57.     WORD stack_segment, stack_offset;
  58.  
  59.     /* save current task context */
  60.     if (setjmp(tasks[current_task]) == 0)
  61.         {
  62.         /* task is now active, and current */
  63.         task_active[id] = YES;
  64.         current_task = id;
  65.  
  66.         /* set new stack for task, copy task pointer to global data */
  67.         /* ensure new stack pointer is at top of stack, and even */
  68.         task_start = task;
  69.         stack_segment = FP_SEG(stack);
  70.         stack_offset = FP_OFF(stack);
  71.  
  72.         /* WARNING!!! SPECIFIC TO BORLAND TURBO-C */
  73.         _SS = stack_segment;
  74.         _SP = stack_offset;
  75.  
  76.         /* execute new task */
  77.         (*task_start)();
  78.  
  79.         /* error, task returned - if using TPAS 4, will have to provide
  80.            an abort function in the Pascal section */
  81.         abort();
  82.         }
  83.     /* current task returns here */
  84.     }
  85.  
  86. /* enable task (allow it to be scheduled) */
  87. VOID PASCAL FAR enable_task(COUNT id)
  88.     {
  89.     task_active[id] = YES;
  90.     }
  91.  
  92. /* disable task (remove from scheduling consideration) */
  93. VOID PASCAL FAR disable_task(COUNT id)
  94.     {
  95.     task_active[id] = NO;
  96.     }
  97.  
  98. /* delay current task by milliseconds - this is only accurate to one timer
  99.    tick (1/18 second) -- multiple tasks can be "blocked" here, each has its
  100.    own copy of the timing variables, and respects its own timeout */
  101. VOID PASCAL FAR delay_task(COUNT milliseconds)
  102.     {
  103.     LONG timeout;
  104.     LONG startsecs, endsecs, now;
  105.     BOOL crossover;
  106.     VOLATILE LONG *t = MK_FP(0x40,0x6C);
  107.  
  108.     startsecs = *t;
  109.  
  110.     /* compute ending time */
  111.     timeout = (LONG)milliseconds * 18L / 1000L;
  112.  
  113.     if (timeout == 0L)
  114.         timeout = 1L;
  115.  
  116.     endsecs = startsecs + timeout;
  117.  
  118.     /* if ending time is past midnight, we have a time crossover
  119.        so adjust endsecs for the new day */
  120.     if (crossover = (endsecs >= 0x1800B0L))
  121.         endsecs = endsecs - 0x1800B0L;
  122.  
  123.     FOREVER
  124.         {
  125.         /* allow other tasks */
  126.         switch_task();
  127.  
  128.         /* get the current time */
  129.         now = *t;
  130.  
  131.         /* see if delay is done */
  132.         if (crossover)
  133.             {
  134.             /* if midnight crossover, exit if time between start and end */
  135.             if (now < startsecs && now >= endsecs)
  136.                 break;
  137.             }
  138.         else
  139.             /* no crossover, exit if time past end */
  140.             if (now >= endsecs)
  141.                 break;
  142.         }
  143.     }
  144.  
  145. /* Initialize the tasker */
  146. VOID PASCAL FAR initialize_task(VOID)
  147.     {
  148.     COUNT i;
  149.  
  150.     /* all tasks inactive */
  151.     for (i = 0; i < MAX_TASKS; ++i)
  152.         task_active[i] = NO;
  153.  
  154.     /* except for the main task */
  155.     task_active[0] = YES;
  156.     current_task = 0;
  157.     }
  158.